package com.zhaowenhao.hello.spring.framework.context;

import com.zhaowenhao.hello.spring.framework.annotation.WHAutowired;
import com.zhaowenhao.hello.spring.framework.annotation.WHController;
import com.zhaowenhao.hello.spring.framework.annotation.WHService;
import com.zhaowenhao.hello.spring.framework.beans.WHBeanWrapper;
import com.zhaowenhao.hello.spring.framework.beans.config.WHBeanDefinition;
import com.zhaowenhao.hello.spring.framework.beans.support.WHBeanDefinitionReader;
import org.omg.PortableServer.POAPackage.ObjectAlreadyActive;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description:
 * @Author: zwh
 * @Date: 2020-08-02
 * @Time: 21:27
 */
public class WHApplicationContext {

    private String[] configLocations = null;

    private WHBeanDefinitionReader reader = null;

    /**
     * IOC容器
     */
    private Map<String, WHBeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<String, WHBeanWrapper>();

    /**
     * 单例的IOC容器缓存
     */
    private Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<String, Object>();

    /**
     * 存储注册信息的BeanDefinition
     */
    protected final Map<String, WHBeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, WHBeanDefinition>();


    public WHApplicationContext(String... configLocations) {
        this.configLocations = configLocations;

        //1 通过reader加载读取配置文件信息，扫描配置类
        reader = new WHBeanDefinitionReader(configLocations);

        //2 得到 将扫描到的类封装为的 BeanDefinitions
        List<WHBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();

        //3 将所有的BeanDefinitions注册到IOC容器中
        doRegisterBeanDefinition(beanDefinitions);

        //4 创建bean实例
        doCreateBean();
    }

    //在创建bean实例的过程中，实际上在getbean方法中完成同时完成依赖注入
    private void doCreateBean() {
        for (Map.Entry<String, WHBeanDefinition> entry : beanDefinitionMap.entrySet()) {
            String beanName = entry.getKey();
            getBean(beanName);
        }
    }

    private void doRegisterBeanDefinition(List<WHBeanDefinition> beanDefinitions) {
        for (WHBeanDefinition definition : beanDefinitions) {
            this.beanDefinitionMap.put(definition.getBeanClassName(), definition);
            this.beanDefinitionMap.put(definition.getFactoryBeanName(), definition);
        }
        //容器初始化完成
    }

    /**
     * 主要完成的工作
     * 1.通过beanname读取beandefinition
     * 2.通过beandefinition来实例化bean
     * 3.封装为beanWrapper
     * 4.依赖注入
     * 5.存入IOC
     * 6.依赖注入
     *
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) {
        //拿到beandefinition
        WHBeanDefinition definition = beanDefinitionMap.get(beanName);

        //通过反射实例化一个对象
        Object instance = instantiate(definition);

        //封装为beanwrapper
        WHBeanWrapper beanWrapper = new WHBeanWrapper(instance);

        //beanwrapper存入IOC容器
        factoryBeanInstanceCache.put(beanName, beanWrapper);

        //依赖注入
        populate(beanWrapper, definition);

        //返回实例对象
        return this.factoryBeanInstanceCache.get(beanName).getWrappedInstance();

    }

    private void populate(WHBeanWrapper beanWrapper, WHBeanDefinition definition) {
        //遍历注解类的所有通过@WHAutowired注解的Field,以 类id 或者 类Type 通过 反射 进行注入

        //拿到实例
        Object instance = beanWrapper.getWrappedInstance();

        //拿到class
        Class<?> clazz = beanWrapper.getWrappedClass();

        //忽略没有加注解的类
        if (!(clazz.isAnnotationPresent(WHService.class) || clazz.isAnnotationPresent(WHController.class))) {
            return;
        }

        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAnnotationPresent(WHAutowired.class)) {
                continue;
            }
            String beanName = field.getAnnotation(WHAutowired.class).value();
            if ("".equals(beanName)) {
                //指定id为空时，按照类型注入
                beanName = field.getType().getName();
            }

            if (!this.factoryBeanInstanceCache.containsKey(beanName)) {
                continue;
            }
            try {
                //强行获得权限
                field.setAccessible(true);
                field.set(instance, this.factoryBeanInstanceCache.get(beanName).getWrappedInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

    }

    private Object instantiate(WHBeanDefinition definition) {
        Object instance = null;
        String beanClassName = definition.getBeanClassName();
        try {
            if (factoryBeanObjectCache.containsKey(beanClassName)) {
                instance = factoryBeanObjectCache.get(beanClassName);
            } else {
                Class<?> clazz = Class.forName(beanClassName);
                instance = clazz.newInstance();
                this.factoryBeanObjectCache.put(beanClassName, instance);
                this.factoryBeanObjectCache.put(definition.getFactoryBeanName(), instance);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return instance;
    }

    public Object getBean(Class<?> beanClass) {
        return getBean(beanClass.getName());
    }

    public String[] getBeanDefinitionNames() {
        return beanDefinitionMap.keySet().toArray(new String[beanDefinitionMap.size()]);
    }

    public int getBeanDefinitonCount() {
        return this.beanDefinitionMap.size();
    }

    public Properties getConfig() {
        return this.reader.getConfig();
    }


}
